
#include "upgrade.h"
#include "usart.h"
#include "crc32.h"
#include "flash_if.h"
#include "stdio.h"
#include "cmd.h"

void mcu_upgrade(void);

static uint16_t write_packet_num = 0;
static uint16_t write_error_count = 0;
static uint8_t upgrade_status = MCU_NOT_UPGRADE_STATUS;

void mcu_begin_upgrade()
{
    write_packet_num = 0;
    write_error_count = 0;
    upgrade_status = MCU_START_UPGRADE_STATUS;
    FLASH_If_Erase(FLASH_UPLOAD_REGION_START_ADDR,FLASH_UPLOAD_REGION_PAGE_NUM); //erase flash 
}

uint8_t get_upgrade_status(void)
{
    return upgrade_status;
}

void set_upgrase_status(uint8_t status)
{
    upgrade_status = status;
}

/*
 * @brief : verify the packet receive from soc.
 * @param : packet data pointer
 * @param : packet length
 * @retval : verify result.
*/
uint32_t packet_verify(uint8_t *data, uint32_t len)
{
    uint32_t cal_crc = 0;
    UpgradePacketHeader *pheader;
    
    pheader = (UpgradePacketHeader *)data;
    
	/* packet data size verify  */
    if (len != PACKET_TOTAL_SIZE)
        return MCU_UPGRADE_PACKET_SIZE_ERROR;
    
	/* packet magic num verify */
    if (pheader->magic_num != PACKET_MAGIC_NUM)
        return MCU_UPGRADE_PACKET_MAGIC_ERROR;
	
	/* total packet num do not exceed half of mcu flash size */
	if (pheader->total_packet_num > PACKET_TOTAL_MAX_NUM)
		return MCU_UPGRADE_PACKET_TOTAL_NUM_ERROR;
    
	/* packet data crc32 verify */
    cal_crc = crc32(data + sizeof(UpgradePacketHeader), PACKET_DATA_SIZE);
    if (cal_crc != pheader->crc32)
        return MCU_UPGRADE_PACKET_CRC_ERROR;
    
    return MCU_UPGRADE_PACKET_VERIFY_OK;
}

uint8_t mcu_upgrade_packet(uint8_t *data, uint16_t len)
{
    uint8_t ack = 0;
    uint8_t status  = 0;
    UpgradePacketHeader *pheader;
    uint8_t *p_source;
    uint8_t packet_num = 0;
    uint8_t total_packet_num ;
    uint32_t flash_addr = 0;
    
    /* verify packet  */
    status = packet_verify(data,len);
    if (status == MCU_UPGRADE_PACKET_VERIFY_OK)
    {
        pheader = (UpgradePacketHeader *)data;
        p_source = data + PACKET_HEADER_SIZE;
        packet_num = pheader->serial_num;
        total_packet_num =  pheader->total_packet_num;
        flash_addr = PACKET_DATA_SIZE * packet_num + FLASH_UPLOAD_REGION_START_ADDR;
        
        /* write data to flash */
        if (FLASH_If_Write(flash_addr, (uint64_t *)p_source, PACKET_DATA_SIZE / 8) \
                    == FLASH_WRITE_SUCCESS)
        {
            write_error_count = 0; //clear error count.
            write_packet_num++;
            ack = MCU_SEND_ACK;
        }
        else
        {
            write_error_count++;
            ack = MCU_WRITE_FLASH_ERROR;
        }
    }
    else
    {
        write_error_count++;
        ack = MCU_VERIFY_PACKET_ERROR;
    }
    
    if (write_error_count > MCU_SEND_PACKET_MAX_ERROR_COUNT)
    {
        upgrade_status = MCU_UPGRADE_FAIL_STATUS;
    }

    /* packet all write.*/
    if (write_packet_num == total_packet_num)
    {
        write_packet_num = 0;
        write_error_count = 0;
        ack = MCU_SEND_UPGRADE_SUCCESS_ACK; //upgrade success
        upgrade_status = MCU_UPGRADE_DONE_STATUS;
    }
    
    return ack;
}

#if 0
int do_mcu_upgrade(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    printf("Begin to upgrade...\r\n");
    FLASH_If_Erase(FLASH_UPLOAD_REGION_START_ADDR,FLASH_UPLOAD_REGION_PAGE_NUM);
    Enable_Upgrade_Receive();
    mcu_upgrade();
    Disable_Upgrade_Receive();
   
    return 0;
}

LYNXI_MCU_CMD(
    upmcu, MAX_CMD_ARG_NUM,1, do_mcu_upgrade,
	"MCU upgrade .",
	"reset"
);
#endif